Skip to main content

Iterable Mapping

Iterable Mapping

You cannot iterate through a mapping. So here is an example of how to create an iterable mapping.

您无法迭代mapping. 这是如何创建可迭代的示例mapping。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

library IterableMapping {
// Iterable mapping from address to uint;
struct Map {
address[] keys;
mapping(address => uint) values;
mapping(address => uint) indexOf;
mapping(address => bool) inserted;
}

function get(Map storage map, address key) public view returns (uint) {
return map.values[key];
}

function getKeyAtIndex(Map storage map, uint index) public view returns (address) {
return map.keys[index];
}

function size(Map storage map) public view returns (uint) {
return map.keys.length;
}

function set(Map storage map, address key, uint val) public {
if (map.inserted[key]) {
map.values[key] = val;
} else {
map.inserted[key] = true;
map.values[key] = val;
map.indexOf[key] = map.keys.length;
map.keys.push(key);
}
}

function remove(Map storage map, address key) public {
if (!map.inserted[key]) {
return;
}

delete map.inserted[key];
delete map.values[key];

uint index = map.indexOf[key];
address lastKey = map.keys[map.keys.length - 1];

map.indexOf[lastKey] = index;
delete map.indexOf[key];

map.keys[index] = lastKey;
map.keys.pop();
}
}

contract TestIterableMap {
using IterableMapping for IterableMapping.Map;

IterableMapping.Map private map;

function testIterableMap() public {
map.set(address(0), 0);
map.set(address(1), 100);
map.set(address(2), 200); // insert
map.set(address(2), 200); // update
map.set(address(3), 300);

for (uint i = 0; i < map.size(); i++) {
address key = map.getKeyAtIndex(i);

assert(map.get(key) == i * 100);
}

map.remove(address(1));

// keys = [address(0), address(3), address(2)]
assert(map.size() == 3);
assert(map.getKeyAtIndex(0) == address(0));
assert(map.getKeyAtIndex(1) == address(3));
assert(map.getKeyAtIndex(2) == address(2));
}
}